2021年更新:
Video.js 文件有更新與 React 整合範例,有加入 使用 useEffect
的寫法。
而除了 Video.js 官方範例外,實務上開發者也會將 Videojs 包成一個自己的 custom hook 再去呼叫,故這篇現在建議看看就好。
今天的內容會用 React + Video.js 去做出自己的 React 播放器元件。
前端在使用 React 開發網站時,有時會碰到第三方套件沒有 React 版本,而需要自己去整理、調整程式碼。
幸運的是:Video.js 文件有與 React 整合的例子 ( 雖然就只有這一頁 )。
使用 create-react-app 建立環境。
建好環境後刪除 src 資料夾不必要的檔案 ( 只留 App.js、index.css、index.js )
因為某些原因,這邊的 Video.js 不使用 NPM 和 Webpack,而是改用 CDN 方式引入,實際大家在開發用 NPM 安裝 Video.js 即可。
在 public 資料夾的 index.html 引入 video.js 檔案
<!-- css -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/video.js/7.2.4/video-js.css">
<!-- js -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/video.js/7.2.4/video.min.js">
</script>
接著設計元件,在 src 資料夾新增一個 ITManPlayer.js,ITManPlayer就會是我們之後的React & Videojs 播放器元件。
將App.js改成這樣
// App.js
import React, { Component } from 'react';
import ITManPlayer from './ITManPlayer';
class App extends Component {
render() {
return (
<div className="wrap">
<ITManPlayer/>
</div>
);
}
}
export default App;
然後到 index.css 加入
/* index.css */
.wrap {
max-width: 800px;
margin-left: auto;
margin-right: auto;
}
接著替 ITManPlayer.js 新增內容。
// ITManPlayer.js
import React, { Component } from "react";
class ITManPlayer extends Component {
constructor() {
super();
this.player = React.createRef();
}
render() {
return (
<video
className="video-js vjs-big-play-centered"
data-setup="{}"
ref={this.player}
/>
);
}
componentDidMount() {
const videoNode = this.player.current;
this.video = window.videojs(videoNode,{
sources: [{
src: 'http://www.html5videoplayer.net/videos/toystory.mp4'
}],
controls: true,
fluid:true
});
}
componentWillUnmount() {
if (this.video) {
this.video.dispose();
}
}
}
export default ITManPlayer;
按照昨日說明,要建立 videojs 物件實體,通常使用videojs()
,而videojs()
第一個參數是 nodeID (為了取的對應的DOM),我們該怎麼在 React 取得對應的 nodeID 呢?
這裡替 render()
內 video 標籤添加 ref 屬姓 ,ref 屬姓是用來獲取 DOM 節點的,媒體播放是少數 React 覺得可以用 ref 來獲取 DOM 並做對應操作的事。
記得在 constructor(){}
內也添加 this.player = React.createRef();
。
按照 Videojs 文件內容,接著在 componentDidMount( )
這個生命週期裡面,我們可以用 this.refs.player
來獲取剛剛用 ref 綁定的DOM。
因為我們在全域用 cdn 引入了 video.js,可以用 window.videojs
來綁定 video 到對應的DOM結點上。
我們可以直接用
window.videojs(this.refs.player)
去綁定 DOM 和新建 video.js 物件了 這裡第一個參數傳入this.refs.player
,第二個參數傳入設定播放器設置的物件。
按照 Videojs 文件內容,在 componentWillUnmount( )
這個生命週期裡面,我們替其加上.dispose()
這個方法,確保我們的 videojs 物件可以在卸除元件時確實被銷毀。
componentWillUnmount() {
if (this.video) {
this.video.dispose();
}
}
此時網頁應該會呈現如下,至此已完成播放器元件的雛型。
接著就是定義<ITManPlayer/>
可傳入屬性與參數,接口部分與團隊討論好即可,以下內容僅供參考:
我希望其他前端在使用<ITManPlayer/>
時,可以像操作 HTML5 播放器寫法一樣,直接在元件標籤上傳入對應的播放器設定,那該怎麼做呢?
例如:
<ITManPlayer
src="http://www.html5videoplayer.net/videos/toystory.mp4"
muted={true}
/>
播放器建立好時要能讀取到這個影片源,並且預設靜音。
這個部分比較簡單,在 ITManPlayer.js 內定義好 props 屬性與預設值,最終把這些參數加入 Video.js 的播放器設置 ( 看是要加進 data-setup
或 videojs()
第二個參數都可以 ),嚴謹一點還可以定義 defaultProps 和 propsType 。
我希望其他前端在使用<ITManPlayer/>
時,可以透過事件監聽(當某事件發生,按當時需求觸發自定義的回呼函式。
而監聽事件名與觸發的回呼函式,理當是從父元件往<ITManPlayer/>
傳入,昨天有提到,Video.js 的 事件監聽寫法會是這樣
videojs物件.on("事件名",之後執行的回呼函式)
所以我們只要在 ITManPlayer.js 內,把上層傳入的參數或值,最終在componentDidMount()
壓成一個一個的.on()
去註冊監聽就好了。
我們可以假定上層元件開一個事件監聽屬性,其他前端工程師使用時會傳入一個物件:
{
事件名稱: 之後會觸發的回呼函式
}
在 ITManPlayer.js 內 props 取得後,會壓成一個一個的
.on(事件名稱,之後會觸發的回呼函式)
例如 App.js 內的<ITManPlayer>
元件這樣寫
<ITManPlayer
src="http://www.html5videoplayer.net/videos/toystory.mp4"
eventON={{
pause: () => {
console.log("暫停");
},
play: () => {
console.log("播放");
}
}}
/>
最終在 componentDidMount()
變成這樣
this.video.on("pause", () => {
console.log("暫停");
});
this.video.on("play", () => {
console.log("播放");
});
我們只要在componentDidMount()
內用Object.keys
與 forEach
去搭配即可完成此一需求。
componentDidMount() {
let { eventON } = this.props;
const videoNode = this.player.current;
this.video = window.videojs(videoNode);
// 監聽事件
if(eventON){
Object.keys(eventON).forEach(item => {
this.video.on(item, () => {
eventON[item]()
});
});
}
}
如果今天在 app.js 新增播放按鈕與暫停按鈕,父元件要如何操縱<ITManPlayer/>
播放器元件呢?
我們需要想辦法暴露<ITManPlayer/>
內的 videojs 物件,讓上層元件也可以去操作它。
這裡一樣利用 ref,把 App.js改成這樣
import React, { Component } from "react";
import ITManPlayer from "./ITManPlayer";
class App extends Component {
videoPlay = () =>{
this.ITManPlayer.video.play();
}
videoPause= () =>{
this.ITManPlayer.video.pause();
}
render() {
return (
<div className="wrap">
<ITManPlayer
ref={player => (this.ITManPlayer = player)}
src="http://www.html5videoplayer.net/videos/toystory.mp4"
eventON={{
pause: () => {
console.log("暫停");
},
play: () => {
console.log("播放");
}
}}
/>
<button onClick={this.videoPlay}>播放</button>
<button onClick={this.videoPause}>暫停</button>
</div>
);
}
}
export default App;
這麼一來,當點擊播放按鈕時,播放按鈕觸發videoPlay
方法,videoPlay
透過 ref 取得的this.ITManPlayer.video
,直接以.play()
方法操作<ITManPlayer/>
播放影片。
今日內容可見我 github 備份